﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace Shoozla.Helper
{
    public class ListWorker<T>
    {
        private List<T> _allItems;
        private int ThreadsNum {get;set;}
        public delegate void ListWorkerDelegate(List<T> list);
        private List<Task> _tasks = new List<Task>();
        private CancellationTokenSource _stopToken = new CancellationTokenSource();
        public event EventHandler ListWorkerDone;

        public ListWorker(List<T> items,int threadsNum)
        {
            _allItems = items;
            ThreadsNum = threadsNum;
            //  ThreadPool.SetMaxThreads(ThreadsNum,ThreadsNum);
        }

        public void SplitAndRun(ParameterizedThreadStart func)
        {
            if (_allItems.Count == 0 || ThreadsNum == 0)
            {
                Trace.WriteLine("[INFO] ListWorker: SplitAndRun: no work to do");
                return;
            }

            int blockSize = _allItems.Count / ThreadsNum;
            if (blockSize <= 0){
                blockSize = _allItems.Count;
                ThreadsNum = 1;
            }

            var ui = TaskScheduler.FromCurrentSynchronizationContext();
            var watch = Stopwatch.StartNew();

            Trace.WriteLine("[INFO] ListWorker: SplitAndRun: Threads count: " + ThreadsNum + " blocksize: " + blockSize);
            for (int i = 0; i < ThreadsNum; i++)
            {
                int realBlockSize= 0;

                if (i == 0)
                    realBlockSize = blockSize;
                else
                {

                    if (i * blockSize >= _allItems.Count)
                        realBlockSize = _allItems.Count - (1 - i) * blockSize;
                    else
                        realBlockSize = blockSize;
                }
                //BLOCK of items


                
                List<T> workingBlock = _allItems.GetRange(i * blockSize, realBlockSize);
                Action a = new Action(()=>{func(workingBlock);});
                Task t = Task.Factory.StartNew(a,_stopToken.Token);
                _tasks.Add(t);

                //Thread t = new Thread(new ParameterizedThreadStart(func));
                //t.SetApartmentState(ApartmentState.STA);
                //t.IsBackground = true;
                //t.Start(func,workingBlock);

                //ThreadPool.QueueUserWorkItem(new WaitCallback(func));
            }

            Task.Factory.ContinueWhenAll(_tasks.ToArray(),
                     result =>
                     {
                         if (ListWorkerDone != null)
                             ListWorkerDone(null, null);
                         Trace.WriteLine("Tasks ended:" + watch.Elapsed);
                     }, CancellationToken.None, TaskContinuationOptions.None, ui);

        }

       
        public void StopThreads()
        {
            _stopToken.Cancel();
           // Task.Factory.CancellationToken.
            //foreach (Thread t in _threads)
            //    t.Abort();
        }
    }
}
